package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.StringUtils;
import com.spzx.common.core.utils.bean.BeanUtils;
import com.spzx.common.redis.cache.GuiguCache;
import com.spzx.product.api.domain.*;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.api.domain.vo.SkuLockVo;
import com.spzx.product.mapper.ProductDetailsMapper;
import com.spzx.product.mapper.ProductMapper;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.IProductService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品Service业务层处理
 */
@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private ProductDetailsMapper productDetailsMapper;

    @Autowired
    private SkuStockMapper skuStockMapper;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    RedissonClient redissonClient;

    /**
     * 查询商品列表
     *
     * @param product 商品
     * @return 商品
     */
    @Override
    public List<Product> selectProductList(Product product) {

        return productMapper.selectProductList(product);
    }


    /**
     * 新增商品
     *
     * @param product
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertProduct(Product product) {
        product.setStatus(0); //线上状态：0-初始值，1-上架，-1-自主下架
        //1.保存Product
        int count1 = productMapper.insert(product);//主键回填

        Long productId = product.getId();

        //2.保存List<ProductSku>
        List<ProductSku> productSkuList = product.getProductSkuList();
        int size = productSkuList.size();
        for (int i = 0; i < size; i++) {
            ProductSku productSku = productSkuList.get(i);
            productSku.setSkuCode(productId + "_" + i);
            productSku.setSkuName(product.getName() + " " + productSku.getSkuSpec());
            productSku.setStatus(0); //线上状态：0-初始值，1-上架，-1-自主下架
            productSku.setProductId(productId);
            productSkuMapper.insert(productSku); //主键回填

            //3.保存List<SkuStock>
            SkuStock skuStock = new SkuStock();
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setAvailableNum(productSku.getStockNum());
            skuStock.setLockNum(0);
            skuStock.setSaleNum(0);
            skuStock.setSkuId(productSku.getId());
            skuStockMapper.insert(skuStock);
        }

        //4.保存详情图片
        ProductDetails productDetails = new ProductDetails();
        productDetails.setProductId(productId);
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.insert(productDetails);
        return 1;
    }


    @Override
    public Product selectProductById(Long id) {
        //1.商品信息 Product
        Product product = productMapper.selectById(id);

        //2.查询List<ProductSku>
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));


        //2.1查询每一个ProductSku对应的库存数据
        List<Long> skuIdList = productSkuList.stream().map(ProductSku::getId).toList();
        List<SkuStock> skuStockList = skuStockMapper
                .selectList(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, skuIdList)
                        .select(SkuStock::getSkuId, SkuStock::getTotalNum));
        Map<Long, Integer> skuIdToTotalNumMap = skuStockList.stream().collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::getTotalNum));

        for (ProductSku productSku : productSkuList) {
            productSku.setStockNum(skuIdToTotalNumMap.get(productSku.getId()));
        }
        product.setProductSkuList(productSkuList);

        //3.查询详情图片ProductDetails
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));
        String imageUrlsStr = productDetails.getImageUrls();
        String[] imageUrlsArray = imageUrlsStr.split(",");
        product.setDetailsImageUrlList(Arrays.asList(imageUrlsArray));
        return product;
    }


    /**
     * 修改商品信息
     *
     * @param product 商品
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateProduct(Product product) {
        //修改商品信息
        productMapper.updateById(product);

        List<ProductSku> productSkuList = product.getProductSkuList();
        productSkuList.forEach(productSku -> {
            //修改商品SKU信息
            productSkuMapper.updateById(productSku);

            //修改商品库存
            SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, productSku.getId()));
            skuStock.setTotalNum(productSku.getStockNum());
            int availableNum = skuStock.getTotalNum() - skuStock.getLockNum();
            skuStock.setAvailableNum(availableNum);
            skuStockMapper.updateById(skuStock);
        });

        //修改商品详细信息
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, product.getId()));
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.updateById(productDetails);
        return 1;
    }


    /**
     * 批量删除商品
     *
     * @param ids 需要删除的商品主键
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int deleteProductByIds(Long[] ids) {
        // 1.删除List<Product>
        productMapper.deleteBatchIds(Arrays.asList(ids));

        //2.删除List<ProductSku>
        List<ProductSku> productSkuList = productSkuMapper
                .selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, ids)
                        .select(ProductSku::getId));
        List<Long> skuIdList = productSkuList.stream().map(ProductSku::getId).toList();
        productSkuMapper.deleteBatchIds(skuIdList);

        //3.删除List<SkuStock>
        skuStockMapper.delete(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, skuIdList));

        //4.删除List<ProductDetails>
        productDetailsMapper.delete(new LambdaQueryWrapper<ProductDetails>().in(ProductDetails::getProductId, ids));
        return 1;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateAuditStatus(Long id, Integer auditStatus) {
        Product product = new Product();
        product.setId(id);
        if (auditStatus == 1) {
            product.setAuditStatus(1);
            product.setAuditMessage("审批通过");
        } else {
            product.setAuditStatus(-1);
            product.setAuditMessage("审批不通过");
        }
        productMapper.updateById(product);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateStatus(Long id, Integer status) {
        Product product = new Product();
        product.setId(id);
        if (status == 1) { //上架
            product.setStatus(1);

            //将上架的商品存放到布隆过滤器中
            RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter("sku:bloom:filter");

            List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));
            for (ProductSku productSku : productSkuList) {
                bloomFilter.add(productSku.getId());
            }

        } else { //下架
            product.setStatus(-1);
        }
        productMapper.updateById(product);
    }


    @Override
    public List<ProductSku> getTopSale() {
        return productSkuMapper.getTopSale();
    }


    @Override
    public List<ProductSku> selectProductSkuList(SkuQuery skuQuery) {
        return productSkuMapper.selectProductSkuList(skuQuery);
    }


    /*@Override
    public ProductSku getProductSku(Long skuId) {
        try {
            //1.从缓存中获取
            String dataKey = "productSku:" + skuId;
            ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
            if (productSku != null) {
                return productSku;
            }
            //2.缓存没有再从数据库中获取
            String lockKey = dataKey + ":lock";
            String lockVal = UUID.randomUUID().toString().replaceAll("-", "");
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal, 3, TimeUnit.SECONDS);
            if (ifAbsent) {
                try {
                    productSku = getSkuFromDB(skuId);
                    long expireTime = productSku == null ? 10 : 60;
                    redisTemplate.opsForValue().set(dataKey, productSku, expireTime, TimeUnit.MINUTES);
                    return productSku;
                } finally {
                    //4.业务执行完毕释放锁
                    String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                            "then\n" +
                            "    return redis.call(\"del\",KEYS[1])\n" +
                            "else\n" +
                            "    return 0\n" +
                            "end";
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(scriptText);
                    redisScript.setResultType(Long.class);
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockVal);
                }
            } else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return getProductSku(skuId);
            }
        } catch (Exception e) { //兜底方案：如果有异常，就从数据库获取返回。
            e.printStackTrace();
            return getSkuFromDB(skuId);
        }
    }*/

    @GuiguCache(prefix = "productSku:")
    @Override
    public ProductSku getProductSku(Long skuId) {
        return productSkuMapper.selectById(skuId);
    }

    //从数据库获取数据
    /*private ProductSku getSkuFromDB(Long skuId) {
        ProductSku productSku = productSkuMapper.selectById(skuId);
        return productSku;
    }*/

    @GuiguCache(prefix = "product:")
    @Override
    public Product getProduct(Long id) {
        return productMapper.selectById(id);
    }

    @GuiguCache(prefix = "skuPrice:")
    @Override
    public SkuPrice getSkuPrice(Long skuId) {
        ProductSku productSku = productSkuMapper.selectOne(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getId, skuId).select(ProductSku::getSalePrice, ProductSku::getMarketPrice));
        SkuPrice skuPrice = new SkuPrice();
        BeanUtils.copyProperties(productSku, skuPrice);
        return skuPrice;
    }

    @GuiguCache(prefix = "productDetails:")
    @Override
    public ProductDetails getProductDetails(Long id) {
        return productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));
    }

    @GuiguCache(prefix = "skuSpecValue:")
    @Override
    public Map<String, Long> getSkuSpecValue(Long id) {
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id).select(ProductSku::getId, ProductSku::getSkuSpec));
        Map<String, Long> skuSpecValueMap = new HashMap<>();
        productSkuList.forEach(item -> {
            skuSpecValueMap.put(item.getSkuSpec(), item.getId());
        });
        return skuSpecValueMap;
    }

    @GuiguCache(prefix = "skuStock:")
    @Override
    public SkuStockVo getSkuStock(Long skuId) {
        SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, skuId));
        SkuStockVo skuStockVo = new SkuStockVo();
        BeanUtils.copyProperties(skuStock, skuStockVo);
        return skuStockVo;
    }


    @Override
    public List<SkuPrice> getSkuPriceList(List<Long> skuIdList) {
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getId, skuIdList));

       return productSkuList.stream().map(productSku -> {
           SkuPrice skuPrice = new SkuPrice();
           BeanUtils.copyProperties(productSku,skuPrice);
           skuPrice.setSkuId(productSku.getId());
           return skuPrice;
        }).toList();

    }

    @Transactional
    @Override
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
        String dataKey = "sku:checkAndlock:data:"+orderNo; //用于锁定库存后将数据存储到redis中使用的key
        String key = "sku:lock:"+orderNo; //用于解决去重问题缓存key.
        //1.去重，通过分布式锁实现
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if(!ifAbsent){ //加不上分布式锁
            if(redisTemplate.hasKey(dataKey)){
                return ""; //没消息就是好消息，获取不到分布式锁，那么，数据被前一个线程存放到缓存了。活干完了。
            }
            return "重复提交";
        }

        //2.先检查库存
        for (SkuLockVo skuLockVo : skuLockVoList) {
            SkuStock skuStock = skuStockMapper.check(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if(skuStock!=null){
                skuLockVo.setIsHaveLock(true); //有库存，可以买
            }else{
                skuLockVo.setIsHaveLock(false); //没有库存，不可以买  因为允许超卖
            }
        }

        //3.锁库存：
        if(skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.getIsHaveLock())){
            StringBuffer result = new StringBuffer();
            //3.2 只要有一条数据检查库存没增加上行锁，那么，锁定库存失败，给调用者返回失败消息。
            //过滤出没有增加上行锁的库存数据，库存不够，无法锁定。
            List<SkuLockVo> noHaveStockSkuLockVoList = skuLockVoList.stream().filter(skuLockVo -> !skuLockVo.getIsHaveLock()).toList();
            for (SkuLockVo noHaveStockSkuLockVo : noHaveStockSkuLockVoList) {
                result.append("[商品skuId=").append(noHaveStockSkuLockVo.getSkuId()).append("]库存不足,锁定失败;");
            }
            redisTemplate.delete(key);
            return result.toString();
        }else{
            //3.1 如果所有库存检查都增加了行锁，可以锁定库存
            for (SkuLockVo skuLockVo : skuLockVoList) {
                int row = skuStockMapper.lock(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
                if(row == 0){
                    redisTemplate.delete(key);
                    throw new ServiceException("锁定库存失败");
                }
            }
        }

        //4.需要将锁定数据存储到redis中，便于后续 解锁库存(下单失败或取消订单) 或  减库存(已支付)。
        redisTemplate.opsForValue().set(dataKey,skuLockVoList);  //没有过期时间，就等着释放。
        return ""; //没有消息，就代表库存锁定成功
    }

    @Transactional(rollbackFor = Exception.class) //事务!!!
    @Override
    public void unlock(String orderNo) {
        String dataKey = "sku:checkAndlock:data:"+orderNo; //解锁缓存key 与  锁定库存后将储到redis中使用的key  一致
        String key = "sku:unlock:"+orderNo; //用于解决去重问题缓存key.
        //1.去重，通过分布式锁实现
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if(!ifAbsent){ //加不上分布式锁不用干活。
            return ;
        }

        //2.获取分布式锁，从redis中获取加锁时存放数据
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>)redisTemplate.opsForValue().get(dataKey);
        if(CollectionUtils.isEmpty(skuLockVoList)){
            redisTemplate.delete(key); //删除分布式锁
            throw new ServiceException("获取缓存加锁数据为空，解锁库存失败");
        }

        //3.开始解锁
        for (SkuLockVo skuLockVo : skuLockVoList) {
            int row = skuStockMapper.unlock(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if(row == 0 ){
                redisTemplate.delete(key); //删除分布式锁
                throw new ServiceException("解锁库存失败");
            }
        }

        //4.删除缓存中加锁的数据，防止再次解锁。
        redisTemplate.delete(dataKey);
    }


    @Transactional(rollbackFor = Exception.class) //事务!!!
    @Override
    public void minus(String orderNo) {
        String dataKey = "sku:checkAndlock:data:"+orderNo; //解锁缓存key 与  锁定库存后将储到redis中使用的key  一致
        String key = "sku:minus:"+orderNo; //用于解决去重问题缓存key.
        //1.去重，通过分布式锁实现
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if(!ifAbsent){ //加不上分布式锁不用干活。
            return ;
        }

        //2.获取分布式锁，从redis中获取加锁时存放数据
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>)redisTemplate.opsForValue().get(dataKey);
        if(CollectionUtils.isEmpty(skuLockVoList)){
            redisTemplate.delete(key); //删除分布式锁
            throw new ServiceException("获取缓存加锁数据为空，解锁库存失败");
        }

        //3.开始减库存
        for (SkuLockVo skuLockVo : skuLockVoList) {
            int row = skuStockMapper.minus(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if(row == 0 ){
                redisTemplate.delete(key); //删除分布式锁
                throw new ServiceException("减库存失败");
            }
        }

        //4.删除缓存中加锁的数据，防止再次减库存。
        redisTemplate.delete(dataKey);
    }
}